package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/smartwalle/alipay/v3"
	"github.com/smartwalle/xid"
)

var client *alipay.Client

const (
	kAppId = "9021000122684093"
	//私钥
	kPrivateKey = "MIIEpAIBAAKCAQEAxMe/M5HuFmgE5HPzphhgHEIda+XIbkLPwd1tSOavGNvWqL/FiNEtQ7DP9j4mcAYKwi6D0MFa1H4gXJ7jETuacvOuat9oW3+/T4CAgAKuwd1+VhGuP+qLIuS8dgLoFMIjp3qqBDWPOwOdRx3Rv7d/BHDvqUjdZpZF8FNiZQhLYBg1nx7wwUZpeWW432cAsyTtTiQuhV/xt9/NYAK4ula4K19L9rsMRChBT4c2XzsvMV9cybc2esuN3hVYHp+L5gAcmjbTT+atPmEGfzSjCTMEo6owpnLFnGsVrJly44tCtkECeZjvg6HSeUtI+3aIbF42VRmZkAscgNJdWTFkdTRtPQIDAQABAoIBADEMFIU3F1/wV1/V4uIAa1w/wnOVV4rcDkqk9UtZbEVG7k8rVYIPmTepB3y56vy7FM1nqKVMvGYAdfKAC9rMv2dtg4jR3IIJ6lZXki/V+1cI7SdoysTVog51ct/7Q3uBFD/3XA8mhmfQM9B5B0HA6+DggWvxZHXgeNg5AGpsUdVGYtRdvsdix/dm/m9yXhUHbRNanI1AsyAGqFhWxqLX7Kblsox9EhLZgYbseMWI4UVxB3e8wO/qddHs00XZTSIgSYajYoYIwEMrZqex8vn84J/ae1VUOz8EJJFndFWN+ZBLmDUoailXRXTuYm306MF/ihGtIW1AOLN16qPbmVP0wWUCgYEA/I7Pvz4eRzrv56kzcxgTmJqNFwooh0snKQDeoPzUitGOs/x1y2FLtApJa7rZkZ+fdp3uvL9SiUgcNVxmy21DF0F9kKCsqHF2ROgan/Rdb+a1Uez5/uD5McIW/CbpwDFb8mzZ6gLddhTl5msiLrqKrno9sd0MGgCEj8pBeTEWCpcCgYEAx3ZS/9ibUQWKZZ4P3CmgcdrKWxuJJ36TLjSvGkT3pU5ebsn9z/C01Vq2PBMz20+8J6rc4qkhYoxfNBzP0DMR/8zGeCZ8Ok4zFasW5gLUYV60SIc8DfD5UUQK7EKS0s1H2awaiktpx9nb8+Y8xMoxASbdTFqIsBp9YZUrvFH0pUsCgYEAq+mCHAlqjs3dc3FHJjbjT+MT2vT9NWrAkzaWVDsFaiDO5FEa1X+7CiheZchgRLHE1/cVBMGuque8282z4xDStEfN8t6L6HSjLrBXPz4VRBBkqAmI+ielOGuHPcg3IK34ioHxq0qb+L6s5zVRpUExGSb4S9PNYv2rPj1MZ/Qzrq0CgYEAu1JYaIaYduLpMr3azz0YvVRorl+R01GAD+jSea1Dli+faBSzFVJNwgFLG+g8zz52yhohD40fGVoTHvHgCPutb7p0ueoKyQcf2r2u55zIsBks3uCCeeVRgAyS5YcwNGEtlTOYSUDBuzrS4p/bekUY+KfzNP3hWOHH1pktVdLyGzkCgYAcVhBLn2wSqi3mhE56tcgURKDjZgYwiSu/RwaF97h7tSkaLWQoOWw+pgb5IXrT//IVHF4lrXMKUoIQwZ6Gh0jBpx2YCvqNlWOAHngA8W1cT5+cnl5wAbpdrmwBXDHCtsbkypMl4bC2WagVsNXunZ/ZCfP+2t4M89ud9Mo1QqMdJQ=="
	// TODO 设置回调地址域名
	kServerDomain = "http://localhost:8090"
)

func main() {
	var err error
	//支付宝提供了用于开发时测试的 sandbox 环境，对接的时候需要注意相关的 app id 和密钥是 sandbox 环境还是 production 环境的。如果是 sandbox 环境，本参数应该传 false，否则为 true。
	if client, err = alipay.New(kAppId, kPrivateKey, false); err != nil {
		log.Println("初始化支付宝失败", err)
		return
	}

	// 加载证书
	// 加载应用公钥证书
	if err = client.LoadAppCertPublicKeyFromFile("appPublicCert.crt"); err != nil {
		log.Println("加载证书发生错误", err)
		return
	}
	// 加载支付宝根证书
	if err = client.LoadAliPayRootCertFromFile("alipayRootCert.crt"); err != nil {
		log.Println("加载证书发生错误", err)
		return
	}
	// 加载支付宝公钥证书
	if err = client.LoadAlipayCertPublicKeyFromFile("alipayPublicCert.crt"); err != nil {
		log.Println("加载证书发生错误", err)
		return
	}
	//接口内容加密
	if err = client.SetEncryptKey("YE9ROv2A/2QsV1AmGsmSuQ=="); err != nil {
		log.Println("加载内容加密密钥发生错误", err)
		return
	}
	//路由函数
	r := gin.Default()
	r.GET("/alipay/pay", pay)
	r.GET("/alipay/callback", callback)
	r.GET("/alipay/notify", notify)

	//http.HandleFunc("/alipay/pay", pay)
	//http.HandleFunc("/alipay/callback", callback)
	//http.HandleFunc("/alipay/notify", notify)

	// http.ListenAndServe(":8090", nil)
	r.Run(":8090")
}

func pay(c *gin.Context) {
	var tradeNo = fmt.Sprintf("%d", xid.Next())

	var p = alipay.TradePagePay{}
	p.NotifyURL = kServerDomain + "/alipay/notify"
	p.ReturnURL = kServerDomain + "/alipay/callback"
	p.Subject = "支付测试:" + tradeNo
	p.OutTradeNo = tradeNo
	p.TotalAmount = "20.00"
	p.ProductCode = "FAST_INSTANT_TRADE_PAY"

	url, _ := client.TradePagePay(p)
	c.Redirect(http.StatusTemporaryRedirect, url.String())
	//http.Redirect(writer, request, url.String(), http.StatusTemporaryRedirect)
}

func callback(c *gin.Context) {
	// 解析请求参数
	if err := c.Request.ParseForm(); err != nil {
		log.Println("解析请求参数发生错误", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "解析请求参数发生错误"})
		return
	}

	// 获取通知参数
	outTradeNo := c.Request.Form.Get("out_trade_no")

	// 验证签名
	log.Printf("form: %v /n", c.Request.Form)
	if err := client.VerifySign(c.Request.Form); err != nil {
		log.Println("回调验证签名发生错误", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "回调验证签名发生错误"})
		return
	}

	log.Println("回调验证签名通过")

	// 查询订单状态
	var p = alipay.TradeQuery{}
	p.OutTradeNo = outTradeNo

	rsp, err := client.TradeQuery(c.Request.Context(), p)
	if err != nil {
		log.Printf("验证订单 %s 信息发生错误: %s", outTradeNo, err.Error())
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("验证订单 %s 信息发生错误: %s", outTradeNo, err.Error())})
		return
	}

	if rsp.IsFailure() {
		log.Printf("验证订单 %s 信息发生错误: %s-%s", outTradeNo, rsp.Msg, rsp.SubMsg)
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("验证订单 %s 信息发生错误: %s-%s", outTradeNo, rsp.Msg, rsp.SubMsg)})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("订单 %s 支付成功", outTradeNo)})
}
func notify(c *gin.Context) {
	// 解析请求参数
	if err := c.Request.ParseForm(); err != nil {
		log.Println("解析请求参数发生错误", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "解析请求参数发生错误"})
		return
	}

	// 解析异步通知
	notification, err := client.DecodeNotification(c.Request.Form)
	if err != nil {
		log.Println("解析异步通知发生错误", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "解析异步通知发生错误"})
		return
	}

	log.Println("解析异步通知成功:", notification.NotifyId)

	// 查询订单状态
	var p = alipay.NewPayload("alipay.trade.query")
	p.AddBizField("out_trade_no", notification.OutTradeNo)

	var rsp *alipay.TradeQueryRsp
	if err := client.Request(c.Request.Context(), p, &rsp); err != nil {
		log.Printf("异步通知验证订单 %s 信息发生错误: %s", notification.OutTradeNo, err.Error())
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("异步通知验证订单 %s 信息发生错误: %s", notification.OutTradeNo, err.Error())})
		return
	}

	if rsp.IsFailure() {
		log.Printf("异步通知验证订单 %s 信息发生错误: %s-%s", notification.OutTradeNo, rsp.Msg, rsp.SubMsg)
		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("异步通知验证订单 %s 信息发生错误: %s-%s", notification.OutTradeNo, rsp.Msg, rsp.SubMsg)})
		return
	}

	log.Printf("订单 %s 支付成功", notification.OutTradeNo)

	client.ACKNotification(c.Writer)
}
